package fs

import (
	"github.com/sirupsen/logrus"
	"mydocker/cgroups/resource"
	"mydocker/common"
	"os"
	"path"
	"strconv"
)

type CpuSubsystem struct {
}

const (
	PeriodDefault = 100000 // 默认周期
	Percent       = 100    // 百分比
)

func (*CpuSubsystem) Name() string {
	return "cpu"
}

func (c *CpuSubsystem) Set(cgroupPath string, res *resource.ResourceConfig) error {
	if res.CpuCFSQuota == 0 {
		return nil
	}
	subsystemCgroupPath, err := getCgroupPath(c.Name(), cgroupPath, true)
	if err != nil {
		logrus.Errorf("get %s path, err %v", cgroupPath, err)
		return err
	}
	logrus.Infof("cpu subsystemCgroupPath: %s", subsystemCgroupPath)
	// cpu.cfs_period_us & cpu.cfs_quota_us 控制的是 CPU 使用时间，单位是微秒，比如每 1 秒钟，这个进程只能使用 200ms，相当于只能用 20% 的 CPU
	if res.CpuCFSQuota != 0 {
		// cpu.cfs_period_us 默认为 100000，即 100ms
		if err = os.WriteFile(path.Join(subsystemCgroupPath, "cpu.cfs_period_us"), []byte(strconv.Itoa(PeriodDefault)), common.Perm0644); err != nil {
			logrus.Errorf("failed to write file cpu.cfs_period_us, err: %+v", err)
			return err
		}
		// cpu.cfs_quota_us 则是根据用户传递的参数来控制，比如参数为 20，就是限制为 20%CPU，所以把 cpu.cfs_quota_us 设置为 cpu.cfs_period_us 的 20% 就行
		// 这里只是简单计算了下，并没有处理一些特殊情况，比如负数什么的
		if err = os.WriteFile(path.Join(subsystemCgroupPath, "cpu.cfs_quota_us"), []byte(strconv.Itoa(PeriodDefault*res.CpuCFSQuota/Percent)), 0644); err != nil {
			logrus.Errorf("failed to write file cpu.cfs_quota_us, err: %+v", err)
		}
	}
	logrus.Infof("cpu.cfs_period_us: %v", res.CpuCFSQuota)
	return nil
}

func (c *CpuSubsystem) Apply(cgroupPath string, pid int) error {
	subsystemCgroupPath, err := getCgroupPath(c.Name(), cgroupPath, true)
	if err != nil {
		logrus.Errorf("get cgroup %s path, err %v", cgroupPath, err)
		return err
	}

	tasksPath := path.Join(subsystemCgroupPath, "tasks")
	if err = os.WriteFile(tasksPath, []byte(strconv.Itoa(pid)), common.Perm0644); err != nil {
		logrus.Errorf("cpu write pid to tasks, path: %s, pid: %d, err: %v", tasksPath, pid, err)
		return err
	}
	return nil
}

func (c *CpuSubsystem) Remove(cgroupPath string) error {
	subsystemCgroupPath, err := getCgroupPath(c.Name(), cgroupPath, false)
	if err != nil {
		logrus.Errorf("get %s path, err %v", cgroupPath, err)
		return err
	}
	return os.RemoveAll(subsystemCgroupPath)
}
